昨天介紹了盡量pass by reference by const這條守則,並說明它可以bypass掉昂貴的construct, destruct操作,也可以避免slicing的問題,今天我們就接續下去,看一下sliced-off問題的實例,以及什麼時候反而應該選用"pass by value"。
我們直接來看看pass by value會遇到的sliced-off問題實例~
假設現在我們有個class:
class Window
{
public:
...
std::string name() const;
virtual void display() const;
};
class WindowWithScrollBars: public Window
{
public:
...
virtual void display() const;
}
可以看到其中display
是virtual的,並且在derived class中有override它,所以我們期望它的行為會依據是Window
or WindowWithScrollBars
而不同。而如果我們這樣將這個object傳入function來處理:
void printNameAndDisplay(Window w)
{
std::cout << w.name();
w.display();
}
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);
後果就是在printNameAndDisplay
裡面,w
會被initialize為Window
物件,而這個function裡面的行為就會走Window::display
那一路,永遠不會走到WindowWithScrollBars::display
。
而如果改用 pass by reference to const
的形式來傳:
void printNameAndDisplay(const Window& w)
{
std::cout << w.name();
w.display();
}
w
就會依照 實際傳入 的物件來行為了!例如此時它就會走WindowWithScrollBars::display
這一路。
在C++ compiler的實作中,reference跟pointer的運作方式很像,而"pass by reference"也實際上就像是"pass by pointer"。
而如果現在function中需要的參數是 built-in type,pass by value通常會更有效率!!因為他們在被設計的時候就是被以 "pass by value" 去考量的。而 iterators 跟 STL中的function object 也適合"pass by value",因為他們的實作則是被設計為能有效地被copy,且也不會有slicing problem的問題。
還有一個要釐清的概念,就是 不是所有"小"的物件pass-by-value就沒差。有些user-defined物件也不大,但不代表它construct, destruct的代價就比較小。例如有些物件中含有pointers,那copy那些物件就包含要copy所有它指向的物件!這個操作代價就很高。
退一步說,即使copy constructor的代價不大,還是有可能會有 performance issues。有些compiler對built-in type跟user-defined type的處理不同,例如就算它跟built-in type包含的東西都相同,但compiler就是不願意把它跟built-in type一樣處理,堅持要用pointers來處理。
還有另一個user-defined type無論大小,最好採用"pass by reference"的原因是,即使現在user-defined type真的很單純代價不大,但user-defined type是可能動態改變的,有可能之後被一直擴充,變得越來越大。
總結來說,大部分情況下本條守則都是成立的,只有 built-in type 跟 STL interator跟function obejct types除外,此外還是盡量使用 "pass by reference to const
" 吧!
貼心重點提醒:
- Prefer pass-by-reference-to-
const
over pass-by-value. It's typically more efficient and it avoids the slicing problem.- The rule doesn't apply to built-in types and STL iterator and function object types. For them, pass-by-value is usually appropriate.
在剛開始學寫程式時感覺比較少有提過這條守則相關的概念,感覺可以翻轉一下,盡量都用refernce來傳遞~ 當然也別矯枉過正─built-in type直接傳就好。